Skip to content

Staging#358

Open
gkorland wants to merge 258 commits intomainfrom
staging
Open

Staging#358
gkorland wants to merge 258 commits intomainfrom
staging

Conversation

@gkorland
Copy link
Contributor

@gkorland gkorland commented Jan 22, 2025

Summary by CodeRabbit

  • New Features
    • Unified desktop/mobile layout with responsive graph and carousel tips; slide-out chat and options drawer; downloadable canvas image and improved search/chat interactions.
  • Bug Fixes
    • Resolved selection, context-menu and input consistency issues; improved path and element actions.
  • Performance Improvements
    • Smoother rendering, improved zoom-to-fit, selective canvas updates and debounced scrolling.
  • Documentation / Dev Ops
    • README updates and added docker-compose + CI/reporting tweaks.
  • Testing
    • Expanded, more resilient end-to-end tests for chat, search, canvas, downloads and tooltips.

@vercel
Copy link

vercel bot commented Jan 22, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
code-graph Ready Ready Preview, Comment Feb 28, 2026 8:54pm

Request Review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 22, 2025

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Chat, graph, UI, tests, and infra were refactored to centralize graph state via GraphRef, expose messaging/path types and state as props, replace the legacy ForceGraph2D with a new ForceGraph/canvas integration, add desktop/mobile refs and UI primitives (carousel/drawer/switch), expand E2E helpers/tests, add docker-compose, and bump dependencies.

Changes

Cohort / File(s) Summary
Chat & Input
app/components/chat.tsx, app/components/Input.tsx
Chat now consumes external state (messages, setMessages, query, setQuery, selectedPath, setSelectedPath, setChatOpen, canvasRef, paths, setPaths). Input removed value prop and reads from node?.name.
Graph UI & Canvas
app/components/code-graph.tsx, app/components/graphView.tsx, app/components/ForceGraph.tsx, app/components/toolbar.tsx, app/components/elementMenu.tsx, app/components/dataPanel.tsx
Unified canvas API using canvasRef: GraphRef; replaced ForceGraph2D with new ForceGraph component wired to @falkordb/canvas; Node/Link unions for selection; handlers added/renamed (handleExpand, handleRemove, zoomedNodes, download hook); toolbar and element/menu/data panel updated to be link-aware.
Model / Types / Utils
app/components/model.ts, lib/utils.ts
Graph model reshaped: new Label interface, Node/Link rework (numeric source/target), labelsMap, and path flags. New shared types and constants (PathData, Message, MessageTypes, GraphRef, PATH_COLOR) moved to lib/utils.
Page & New UI primitives
app/page.tsx, components/ui/carousel.tsx, components/ui/drawer.tsx, components/ui/switch.tsx, app/components/combobox.tsx
Desktop/mobile refs split; Chat/CodeGraph wired to expanded props; new Carousel, Drawer, Switch components added; combobox styling tweaked and mobile UI flows (drawer/carousel) introduced.
Styling & Config
app/globals.css, tailwind.config.js, components.json, app/layout.tsx
New chart CSS vars and .control-button; Tailwind chart palette added; new import aliases (ui, lib, hooks) and iconLibrary in components.json; removed GoogleAnalytics import.
E2E tests & helpers
e2e/logic/POM/codeGraph.ts, e2e/logic/utils.ts, e2e/config/*, e2e/tests/*.spec.ts, e2e/infra/*
POMs made mobile-aware and scoped; new wait helpers (waitForStableText, waitForElementToBeVisible, interactWhenVisible); canvas helpers (downloadImage, rightClickAtCanvasCenter, hoverAtCanvasCenter); tests updated/added for download, tooltips, chat consistency; constants and test data expanded.
Playwright & CI
.github/workflows/playwright.yml, playwright.config.ts, .github/workflows/nextjs.yml
CI changes: Node version bumps to 24, ensure report dirs, upload failed screenshots artifact, change reporter/workers and adjust Playwright outputs.
Docker, README & deps
docker-compose.yml, README.md, package.json, Dockerfile
Added docker-compose (falkordb + backend), README restructure, dependency additions/bumps (e.g., @falkordb/canvas, embla, html2canvas, react-json-tree, vaul), and Docker base image updated to Node 24.
API
app/api/repo/route.ts
Enabled POST handler using NextRequest that proxies to analyze_folder or analyze_repo based on repo_url, with unified error handling.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ChatUI as Chat component
    participant API as /api/chat (or repo)
    participant Canvas as ForceGraph via GraphRef

    User->>ChatUI: submit question or request path
    ChatUI->>API: fetch response / path data
    API-->>ChatUI: returns messages and path elements
    ChatUI->>Canvas: merge/update graph data via canvasRef (set data, mark path flags)
    Canvas-->>ChatUI: engine stop / zoomToFit complete (events)
    ChatUI-->>User: render messages and focused graph
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • Staging #315 — Overlapping, code-level changes to chat, graph components, model, and canvas integration; likely closely related to GraphRef and message/path type work.

Suggested reviewers

  • AviAvni

Poem

🐰 I hopped through props and typed each name,
I nudged the canvas to zoom and call its frame.
Drawers and carousels spun bright in view,
Paths lit in PATH_COLOR — a golden hue.
Tests danced, CI hummed — I left a carrot too. 🥕

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Staging' is extremely vague and generic, providing no meaningful information about the actual changeset contents. Replace with a descriptive title that captures the main changes, such as 'Refactor Chat and CodeGraph components with external state management and canvas integration' or 'Add responsive UI layer and graph visualization improvements'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch staging

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Summary

Hi! Looks like you've reached your API usage limit. You can increase it from your account settings page here: app.greptile.com/settings/billing/code-review

💡 (1/5) You can manually trigger the bot by mentioning @greptileai in a comment!

9 file(s) reviewed, no comment(s)
Edit PR Review Bot Settings | Greptile

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🔭 Outside diff range comments (2)
app/components/graphView.tsx (1)

Line range hint 105-166: Improve double-click detection implementation.

The current implementation has potential issues:

  1. The double-click detection uses a fixed 1000ms threshold which might not match the system's double-click speed setting.
  2. The implementation doesn't handle the case where a user clicks different nodes within the threshold.

Consider this improved implementation:

-const lastClick = useRef<{ date: Date, name: string }>({ date: new Date(), name: "" })
+const lastClick = useRef<{ time: number, id: number }>({ time: 0, id: 0 })

-const isDoubleClick = now.getTime() - date.getTime() < 1000 && name === node.name
+const isDoubleClick = (now.getTime() - lastClick.current.time < 500) && (lastClick.current.id === node.id)
+lastClick.current = { time: now.getTime(), id: node.id }
app/components/chat.tsx (1)

Line range hint 157-215: Improve zoom functionality implementation.

Several improvements can be made to the zoom functionality:

  1. The setTimeout with 0ms delay is unnecessary
  2. Magic numbers should be constants
  3. Add proper error handling for undefined chart

Apply these changes:

+const ZOOM_DURATION = 1000;
+const ZOOM_PADDING = 150;

 const handleSetSelectedPath = (p: PathData) => {
   const chart = chartRef.current
   
-  if (!chart) return
+  if (!chart) {
+    console.warn('Chart reference is not available');
+    return;
+  }
   
   // ... rest of the function ...
   
-  setTimeout(() => {
-    chart.zoomToFit(1000, 150, (n: NodeObject<Node>) => p.nodes.some(node => node.id === n.id));
-  }, 0)
+  chart.zoomToFit(ZOOM_DURATION, ZOOM_PADDING, (n: NodeObject<Node>) => p.nodes.some(node => node.id === n.id));
 }
🧰 Tools
🪛 Biome (1.9.4)

[error] 163-163: Comparing to itself is potentially pointless.

(lint/suspicious/noSelfCompare)

🧹 Nitpick comments (5)
e2e/tests/chat.spec.ts (1)

113-113: Remove unnecessary empty line within the loop.

The empty line within the loop affects readability without adding value.

    for (let i = 0; i < 3; i++) {
      await chat.sendMessage(Node_Question);
      const result = await chat.getTextInLastChatElement();
      const number = result.match(/\d+/g)?.[0]!;
      responses.push(number);
-      
    }
app/components/model.ts (2)

179-215: Add error handling for edge cases.

While the node creation logic is sound, it would benefit from additional error handling for edge cases.

      let source = this.nodesMap.get(edgeData.src_node)
      let target = this.nodesMap.get(edgeData.dest_node)

+     if (edgeData.src_node === undefined || edgeData.dest_node === undefined) {
+       console.warn('Invalid edge data: Missing source or destination node ID');
+       return;
+     }

      if (!source) {

229-253: Add documentation for curve calculation logic.

The curve calculation logic is complex and would benefit from documentation explaining the mathematical reasoning.

Add a comment block explaining the curve calculation:

+    // Calculate curve values for graph edges:
+    // - For self-loops (start === end):
+    //   - Even indices: negative values starting from -3
+    //   - Odd indices: positive values starting from 2
+    // - For regular edges:
+    //   - Even indices: negative values starting from 0
+    //   - Odd indices: positive values starting from 1
+    // The final curve value is scaled by 0.1 to create subtle curves
     newElements.links.forEach(link => {
e2e/logic/POM/codeGraph.ts (1)

284-285: Consider removing redundant delay.

The code waits for the loading image to be hidden and then adds an additional 2-second delay, which might be unnecessary.

Consider removing the redundant delay:

 async getTextInLastChatElement(): Promise<string>{
     await this.page.waitForSelector('img[alt="Waiting for response"]', { state: 'hidden' });
-    await delay(2000);
     return (await this.lastElementInChat.textContent())!;
 }
app/components/chat.tsx (1)

Line range hint 272-313: Extract common zoom functionality.

The zoom logic is duplicated between handleSetSelectedPath and handleSubmit. Consider extracting it into a reusable function.

Apply these changes:

+const ZOOM_DURATION = 1000;
+const ZOOM_PADDING = 150;
+
+const zoomToNodes = (chart: ForceGraphMethods, nodes: any[]) => {
+  if (!chart) {
+    console.warn('Chart reference is not available');
+    return;
+  }
+  chart.zoomToFit(ZOOM_DURATION, ZOOM_PADDING, (n: NodeObject<Node>) => 
+    nodes.some(node => node.id === n.id)
+  );
+};

 const handleSubmit = async () => {
   const chart = chartRef.current
   
   if (!chart) return
   
   // ... rest of the function ...
   
-  setTimeout(() => {
-    chart.zoomToFit(1000, 150, (n: NodeObject<Node>) => 
-      formattedPaths.some(p => p.nodes.some(node => node.id === n.id))
-    );
-  }, 0)
+  zoomToNodes(chart, formattedPaths.flatMap(p => p.nodes));
 }

 const handleSetSelectedPath = (p: PathData) => {
   // ... rest of the function ...
-  setTimeout(() => {
-    chart.zoomToFit(1000, 150, (n: NodeObject<Node>) => 
-      p.nodes.some(node => node.id === n.id)
-    );
-  }, 0)
+  zoomToNodes(chart, p.nodes);
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f419fe6 and d47f502.

📒 Files selected for processing (9)
  • app/components/chat.tsx (9 hunks)
  • app/components/code-graph.tsx (5 hunks)
  • app/components/elementMenu.tsx (3 hunks)
  • app/components/graphView.tsx (5 hunks)
  • app/components/model.ts (3 hunks)
  • app/page.tsx (1 hunks)
  • e2e/logic/POM/codeGraph.ts (4 hunks)
  • e2e/tests/canvas.spec.ts (1 hunks)
  • e2e/tests/chat.spec.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: build
  • GitHub Check: test
  • GitHub Check: test
🔇 Additional comments (11)
e2e/tests/chat.spec.ts (1)

120-121: LGTM! Good test isolation.

Moving the API calls inside the test scope improves test isolation and follows testing best practices.

app/components/elementMenu.tsx (1)

17-17: LGTM! Consistent function naming.

The typo fix from handelExpand to handleExpand is applied consistently across the component.

Also applies to: 22-22, 71-71, 77-77, 117-117, 123-123

app/components/model.ts (1)

55-55: LGTM! Safe default parameter.

Adding a default value of 0 ensures the function always returns a valid color.

app/page.tsx (1)

284-284: LGTM! Enhanced graph interaction.

Adding the chartRef prop to the Chat component enables improved graph visualization capabilities.

e2e/tests/canvas.spec.ts (1)

46-46: LGTM! Good improvement to test reliability.

Adding clickCenter() before zoom operations ensures consistent test behavior by establishing a known starting position.

app/components/graphView.tsx (2)

64-87: Good implementation of responsive graph sizing.

The resize observer implementation properly handles both window resizing and parent container changes. The cleanup in the useEffect hook correctly removes event listeners and disconnects the observer.


173-174: LGTM! Proper usage of dynamic dimensions.

Using state-managed dimensions ensures the graph properly responds to container size changes.

app/components/code-graph.tsx (1)

Line range hint 244-357: LGTM! Good cleanup of handler names.

The renaming from 'handel*' to 'handle*' improves code consistency and fixes typos.

e2e/logic/POM/codeGraph.ts (2)

155-157: LGTM! Good addition of loading state locator.

The new locator helps track loading states consistently across tests.


426-426: Verify the impact of changing to right-click.

The change from left-click to right-click for node selection might affect other tests that depend on this behavior.

Let's verify the impact:

✅ Verification successful

Right-click change appears safe to implement

The tests use the nodeClick() abstraction method rather than direct click events, focusing on outcomes rather than the click mechanism. The change from left to right click is an implementation detail that won't affect the test behavior as long as the nodeClick() method maintains its core functionality of node selection.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for tests that might be affected by the click behavior change
rg -l "nodeClick" "e2e/tests"

Length of output: 87


Script:

#!/bin/bash
# Check the content of test files for click-related code
echo "=== canvas.spec.ts ==="
rg -A 5 -B 5 "click" "e2e/tests/canvas.spec.ts"
echo -e "\n=== nodeDetailsPanel.spec.ts ==="
rg -A 5 -B 5 "click" "e2e/tests/nodeDetailsPanel.spec.ts"

Length of output: 8469

app/components/chat.tsx (1)

110-110: LGTM!

The function signature has been correctly updated to include the new chartRef prop.

… handling and neighbor deletion logic. Updated deleteNeighbors function to handle expanded nodes correctly and replaced direct calls with handleExpand in GraphView for better clarity and maintainability.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🔭 Outside diff range comments (1)
app/components/Input.tsx (1)

Line range hint 201-201: Warning: Dynamic Tailwind class names may not work in production.

The dynamic class name bg-${colorName} might not survive Tailwind's purge in production. Consider using style objects or safelist these color classes in your Tailwind config.

Example safelist configuration in tailwind.config.js:

module.exports = {
  safelist: [
    {
      pattern: /bg-(red|blue|green|yellow|purple|pink|indigo|gray)-/,
      variants: ['hover', 'focus', 'active']
    }
  ]
}
🧹 Nitpick comments (1)
app/components/Input.tsx (1)

Line range hint 147-160: Consider extracting input validation logic.

While the validation works correctly, consider extracting it into a reusable function for better maintainability.

+const INVALID_CHARS_PATTERN = /[%*()\-\[\]{};:"|~]/;
+const getInvalidCharacterMessage = (char: string) => 
+  `The character "${char}" is not allowed in this field.`;
+
+const validateInput = (value: string): { isValid: boolean; message: string } => {
+  const match = value.match(INVALID_CHARS_PATTERN);
+  return match 
+    ? { isValid: false, message: getInvalidCharacterMessage(match[0]) }
+    : { isValid: true, message: '' };
+};
+
 onChange={(e) => {
     const newVal = e.target.value
-    const invalidChars = /[%*()\-\[\]{};:"|~]/;
-
-    if (invalidChars.test(newVal)) {
-        e.target.setCustomValidity(`The character "${newVal.match(invalidChars)?.[0]}" is not allowed in this field.`);
+    const { isValid, message } = validateInput(newVal);
+    if (!isValid) {
+        e.target.setCustomValidity(message);
         e.target.reportValidity();
         return;
     }
     e.target.setCustomValidity('');
     onValueChange({ name: newVal })
 }}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d47f502 and 43c94c7.

📒 Files selected for processing (1)
  • app/components/Input.tsx (6 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: test
  • GitHub Check: test
🔇 Additional comments (2)
app/components/Input.tsx (2)

18-18: LGTM! Good architectural improvement.

The removal of the value prop in favor of using node?.name makes the component more focused and reduces prop drilling.


Line range hint 48-79: LGTM! Well-implemented search functionality.

The implementation includes:

  • Proper debouncing with timeout
  • Clean error handling with user feedback
  • Appropriate cleanup in useEffect

Anchel123 and others added 25 commits February 24, 2026 12:23
…t handlers for improved link and node interactions
Update dependencies and improve test configurations
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 17

🧹 Nitpick comments (9)
.github/dependabot.yml (1)

13-16: Group name suggests minor updates but only patches are included.

The group is named npm-minor-patch but only includes "patch" in update-types. This naming could be confusing. Consider either:

  1. Renaming to npm-patch if only patch updates are intended, or
  2. Adding "minor" to update-types if minor updates should also be grouped.
💡 Option 1: Rename to match behavior
     groups:
-      npm-minor-patch:
+      npm-patch:
         update-types:
           - "patch"
💡 Option 2: Include minor updates
     groups:
       npm-minor-patch:
         update-types:
+          - "minor"
           - "patch"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/dependabot.yml around lines 13 - 16, The dependabot group name
npm-minor-patch does not match its update-types (only "patch"); either rename
the group to npm-patch or add "minor" to update-types to match intent—update the
groups -> npm-minor-patch name or modify the update-types array (the keys
involved are groups, npm-minor-patch and update-types) accordingly so the config
and naming are consistent.
e2e/logic/utils.ts (2)

52-54: Consider adding type annotations for improved type safety.

The function signature uses any[] and returns any, which reduces type safety. If the node structure is known, consider using a more specific type or generic.

💡 Optional: Type-safe alternative
-export function findNodeByName(nodes: any[], nodeName: string): any {
+export function findNodeByName<T extends { name?: string; data?: { name?: string } }>(
+  nodes: T[], 
+  nodeName: string
+): T | undefined {
     return nodes.find((node) => node.name === nodeName || node.data?.name === nodeName);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/logic/utils.ts` around lines 52 - 54, The findNodeByName function
currently uses any[] and any; replace these with a type-safe signature by
introducing a Node-like shape or a generic: define or use an interface with
optional fields name?: string and data?: { name?: string } and change
findNodeByName to accept nodes of that type (or a generic T extends that shape)
and return T | undefined instead of any; update any callsites if necessary to
satisfy the new return type.

16-35: Consider edge case: function returns potentially unstable text.

The function returns stableText at the end even if the text never stabilized (i.e., it kept changing until timeout). This might be the intended behavior, but consider throwing an error or logging a warning when timeout is reached without stabilization to help debug flaky tests.

💡 Optional: Add timeout warning
     for (let i = 0; i < maxChecks; i++) {
         stableText = await locator.textContent() ?? "";
         if (stableText === previousText && stableText.trim().length > 0) {
             return stableText;
         }
         previousText = stableText;
         await locator.page().waitForTimeout(pollingInterval);
     }
 
+    console.warn(`Text did not stabilize within ${timeout}ms, returning last value`);
     return stableText;
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/logic/utils.ts` around lines 16 - 35, waitForStableText currently returns
the last-read stableText even if the loop timed out without stabilization;
update the end of waitForStableText to treat timeout as a failure: throw an
Error (or at minimum log a warning) indicating the text never stabilized,
include contextual info (the last observed stableText, locator description or
string, and the timeout value) so tests can fail fast and debugging is easier;
locate this logic in the waitForStableText function and replace the final return
stableText with a thrown error or a process/test logger call as appropriate for
the test runner.
package.json (1)

29-29: Add @types/d3 to devDependencies for TypeScript support.

While d3@^7.9.0 does not include built-in TypeScript declarations, d3 is not currently imported in the codebase. However, since the library is now a dependency, adding types ahead of time will prevent TypeScript compilation issues when it's used. Install via DefinitelyTyped:

"@types/d3": "^7.4.3"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` at line 29, Add the DefinitelyTyped declarations for D3 by
adding "@types/d3": "^7.4.3" to devDependencies in package.json so TypeScript
has typings when d3 is imported; update the package.json devDependencies block
(referencing the existing "d3" dependency) and run npm/yarn install to persist
the change.
lib/utils.ts (3)

2-2: Unused import: GraphNode.

GraphNode is imported from @falkordb/canvas but is not used anywhere in this file.

♻️ Proposed fix
-import FalkorDBCanvas, { GraphNode } from "@falkordb/canvas"
+import FalkorDBCanvas from "@falkordb/canvas"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/utils.ts` at line 2, The import includes an unused symbol GraphNode from
`@falkordb/canvas`; remove GraphNode from the import statement (leave
FalkorDBCanvas) to eliminate the unused import warning and keep imports minimal.

7-10: Consider stronger typing instead of any[].

Using any[] for nodes and links reduces type safety. Consider using the imported Node and Link types for better compile-time checks.

♻️ Proposed fix
+import { Link, Node } from "@/app/components/model"
+
 export type PathData = {
-  nodes: any[]
-  links: any[]
+  nodes: Node[]
+  links: Link[]
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/utils.ts` around lines 7 - 10, The PathData type currently uses any[] for
nodes and links which reduces type safety; update the PathData definition in
lib/utils.ts to use the specific imported Node and Link types (i.e., nodes:
Node[] and links: Link[]), add/import those types if they are not already
imported, and adjust any call sites or tests that relied on any[] to satisfy the
stronger types.

31-36: Consider stronger typing for paths property.

The paths property uses any[] for both nodes and links. This could leverage the PathData type defined above for consistency and type safety.

♻️ Proposed fix
 export interface Message {
   type: MessageTypes;
   text?: string;
-  paths?: { nodes: any[], links: any[] }[];
+  paths?: PathData[];
   graphName?: string;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/utils.ts` around lines 31 - 36, The Message interface's paths property is
weakly typed using any[]; replace it with the existing PathData type for
stronger typing: change paths?: { nodes: any[], links: any[] }[] to paths?:
PathData[] (or Array<PathData>) in the Message interface so it uses the PathData
definition above and preserves optionality. Ensure PathData is visible in the
same module (or imported) so the Message type compiles.
app/components/toolbar.tsx (1)

64-64: Inconsistent title casing.

The title attribute uses camelCase "downloadImage" while other buttons use title case like "Zoom In", "Zoom Out", and "Center". Consider using consistent casing for accessibility and consistency.

♻️ Proposed fix
             <button
                 className="hidden md:block control-button"
-                title="downloadImage"
+                title="Download Image"
                 onClick={handleDownloadImage}
             >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/toolbar.tsx` at line 64, The title attribute for the download
button ("downloadImage") is using camelCase while other buttons use Title Case,
causing inconsistent accessibility labels; update the title attribute in the
toolbar component (the element with title="downloadImage") to use Title Case
e.g. "Download Image" to match "Zoom In"/"Zoom Out"/"Center" and ensure
consistency across the toolbar UI.
app/api/repo/route.ts (1)

45-45: Hardcoded ignore list may not be appropriate for all repositories.

The ignore array is hardcoded with specific directory patterns. This may not apply universally to all repositories being analyzed. Consider making this configurable via environment variables or request parameters.

♻️ Proposed configuration approach
+const DEFAULT_IGNORE = ["./.github", "./sbin", "./.git", "./deps", "./bin", "./build"];
+
 export async function POST(request: NextRequest) {
   const repo_url = request.nextUrl.searchParams.get('url');
+  const customIgnore = request.nextUrl.searchParams.get('ignore');
+  const ignore = customIgnore ? JSON.parse(customIgnore) : DEFAULT_IGNORE;
   // ...
   const result = await fetch(`${url}/${isLocal ? "analyze_folder" : "analyze_repo"}`, {
     method: 'POST',
-    body: JSON.stringify({ repo_url, ignore: ["./.github", "./sbin", "./.git", "./deps", "./bin", "./build"] }),
+    body: JSON.stringify({ repo_url, ignore }),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/api/repo/route.ts` at line 45, The hardcoded ignore array in the request
body (body: JSON.stringify({ repo_url, ignore: [...] })) should be made
configurable: update the handler in app/api/repo/route.ts (the function that
constructs this body) to accept an optional ignore list from the incoming
request payload (e.g., request.body.ignore) and/or read a fallback from an
environment variable (e.g., REPO_IGNORE_LIST as JSON or comma-separated); if
neither is provided, fall back to the current default array. Validate/parses the
env or request value into an array before including it in JSON.stringify so
existing behavior remains unchanged when no config is supplied.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/components/chat.tsx`:
- Line 100: The overlap check uses the same identifier for both elements so e.id
=== e.id is always true; update the inner some() callback to use a different
variable name (e.g., prevElem) and compare e.id === prevElem.id so you're
comparing an element from the current path (paths.some path -> e) against
elements from the previous selection ([...prev.nodes, ...prev.links] ->
prevElem). Locate the lambda in the paths.some(... every(... some(...)))
expression in chat.tsx and rename the inner callback parameter and its
comparison to fix the logic.

In `@app/components/code-graph.tsx`:
- Around line 106-110: The Delete-key guard in handleKeyDown is inverted: it
currently returns early when selectedObj exists and selectedObjects is empty,
preventing single-selection deletion; update the condition so it only returns
early when there is neither a single selectedObj nor any selectedObjects (i.e.,
if (!selectedObj && selectedObjects.length === 0) return), then call
handleRemove with the combined ids computed from selectedObjects.map(obj =>
obj.id) and selectedObj?.id filtered for undefined, ensuring you still pass
"nodes" as the second arg.

In `@app/components/dataPanel.tsx`:
- Around line 21-26: The excludedProperties array in dataPanel.tsx contains a
duplicate entry "isPathSelected"; remove the redundant "isPathSelected" so that
excludedProperties only lists each property once (locate the array named
excludedProperties in app/components/dataPanel.tsx and delete the duplicate
string entry).
- Around line 95-112: The valueRenderer is declared with parameters
(valueAsString, value, keyPath) but react-json-tree passes keyPath as rest
parameters; update the valueRenderer implementation (the prop named
valueRenderer) to accept rest parameters (e.g., (...keyPath)) and then check the
first element keyPath[0] === "src" to decide when to render the
SyntaxHighlighter in the DataPanel component; ensure the fallback still returns
the span with className "text-white".

In `@app/components/graphView.tsx`:
- Around line 167-168: The current checks use "!node.x || !node.y" which
incorrectly treats 0 as missing; update both occurrences where that check
appears (the conditional guarding rendering/hit-testing of a node) to explicitly
test for null/undefined instead, e.g. use "node.x == null || node.y == null" or
"node.x === undefined || node.y === undefined" (or prefer
Number.isFinite(node.x) && Number.isFinite(node.y) if you want to ensure numeric
coordinates) so nodes at x=0 or y=0 are not skipped.

In `@app/components/model.ts`:
- Around line 197-230: When synthesizing missing endpoint nodes (the local
variables source and target created from edgeData.src_node / edgeData.dest_node)
you currently only set them in this.nodesMap; also append the newly created node
objects to the exported element lists (e.g. this.elements.nodes and, if present
in context, newElements.nodes) so links reference actual exported nodes; update
the blocks that create source and target to push the created node into
this.elements.nodes (and newElements.nodes when used) immediately after
this.nodesMap.set(...) to keep maps and element arrays in sync.

In `@app/page.tsx`:
- Around line 371-388: External links rendered with the Link components (e.g.,
the Link instances that render the FalkorDB logo, "Main Website", "Github",
"Discord" etc. in app/page.tsx) use target='_blank' but lack rel='noopener
noreferrer'; update each external Link element that opens in a new tab to
include rel="noopener noreferrer" alongside target='_blank' to prevent
tabnabbing and improve security—ensure every Link with target='_blank' (all
occurrences around the header block and lines ~537-560) is amended accordingly.
- Line 617: The mobile CodeGraph is calling onCategoryClick with the desktop
canvas ref (desktopChartRef) so mobile category toggles target the wrong canvas;
update the mobile CodeGraph invocation to pass the mobile canvas ref (e.g.,
mobileChartRef or the ref used by the mobile CodeGraph instance) into
onCategoryClick instead of desktopChartRef, ensuring the onCategoryClick(name,
show, ...) call for the mobile component uses the mobile ref; search for the
CodeGraph mobile render and replace the passed desktopChartRef with the
mobileChartRef symbol (or the precise ref variable name used for mobile).

In `@e2e/logic/POM/codeGraph.ts`:
- Line 152: The XPath in locateNodeInLastChatPath is invalid because ${node} is
injected unquoted; wrap the node string in quotes and escape any internal quotes
— e.g., interpolate a properly quoted JS string using JSON.stringify(node) so
the XPath becomes contains(text(), ${JSON.stringify(node)}); update the locator
returned by locateNodeInLastChatPath accordingly to ensure safe, valid XPath.
- Around line 595-605: The function always calls window.graphDesktop() which
returns the wrong instance in mobile tests; update the accessor selection so
that both the waitForFunction check and the final page.evaluate call use
window.graphMobile() when running in mobile mode and window.graphDesktop()
otherwise—detect mobile via the Playwright context/page mode available in this
class (e.g., this.page.isMobile or other environment flag) and replace the
hardcoded graphDesktop references (graphDesktop, graphMobile, page.evaluate,
page.waitForFunction) so the code conditionally invokes the correct window graph
accessor before awaiting canvas animation and returning the graph data.

In `@e2e/tests/canvas.spec.ts`:
- Around line 222-225: Replace unsafe non-null assertions on firstNodeRes and
secondNodeRes when calling api.showPath and in expectations: after asserting
firstNodeRes and secondNodeRes are defined (using expect(...).toBeDefined()),
extract their ids with a guard (e.g., throw or return if undefined) or use
optional chaining when accessing .id, and use those guarded ids in the
api.showPath call and subsequent checks for callsRelationObject (from
response.result.paths[0].find). Also guard callsRelationObject before asserting
src_node/dest_node to avoid runtime errors if the relation is missing.

In `@e2e/tests/chat.spec.ts`:
- Around line 85-87: The test uses a non-null assertion on const number =
uiResponse.match(/\d+/g)?.[0]! which can throw if the regex doesn't match and
also directly compares to apiResponse.result.response.match(...), so update the
assertion to avoid the trailing "!" and first capture both matches into
temporaries (e.g., uiMatch and apiMatch), assert each is defined (or fail with a
clear message) and then compare them; reference the variables uiResponse,
apiResponse, and the extracted value currently named number to locate the lines
to change and ensure robust null/undefined handling before calling
expect(...).toEqual(...).
- Line 69: The line "const number = result.match(/\d+/g)?.[0]!" uses a non-null
assertion after optional chaining which is unsafe; change it to explicitly
handle the possible null/undefined match: call result.match(/\d+/g) into a local
variable (e.g., const match = result.match(/\d+/g)), assert or throw if match is
null or match[0] is falsy (or use an expect(...) assertion in the test), then
assign const number = match[0]; this removes the "!.", ensures deterministic
failures with a clear error message, and references the existing identifiers
"number" and the result.match(...) expression in chat.spec.ts.

In `@package.json`:
- Line 33: package.json pins Next.js to "next": "^15.5.8" which has security
advisories and may require React 19 for App Router; update the "next" dependency
to a patched release (>=15.5.9) and, if your app uses the App Router, bump
"react" and "react-dom" to React 19-compatible versions (replace current "react"
and "react-dom" entries) and run install and test to resolve peer dependency
warnings and verify the app and App Router behavior.

In `@playwright.config.ts`:
- Around line 22-23: The comment above the workers setting is stale: update the
comment to accurately describe the current behavior of the workers property
(workers: process.env.CI ? 3 : undefined) — e.g., change the comment to
something like "Use a fixed 3 workers on CI (otherwise default)" or similar so
it no longer says we opt out of parallel tests; ensure you edit the comment
immediately above the workers property that references the workers setting.

In `@README.md`:
- Line 109: Update the GitHub Issues link in the README: replace the current URL
"https://github.com/FalkorDB/GraphRAG-SDK/issues" referenced by the "[GitHub
Issues]" list item with "https://github.com/FalkorDB/code-graph/issues" so the
repo's README points to the correct issue tracker for code-graph.
- Line 9: Remove the stray lone "-" character present in the README (the stray
hyphen on its own line) so the file has no extraneous characters; simply delete
that line containing only "-" and save the README to restore clean formatting.

---

Nitpick comments:
In @.github/dependabot.yml:
- Around line 13-16: The dependabot group name npm-minor-patch does not match
its update-types (only "patch"); either rename the group to npm-patch or add
"minor" to update-types to match intent—update the groups -> npm-minor-patch
name or modify the update-types array (the keys involved are groups,
npm-minor-patch and update-types) accordingly so the config and naming are
consistent.

In `@app/api/repo/route.ts`:
- Line 45: The hardcoded ignore array in the request body (body:
JSON.stringify({ repo_url, ignore: [...] })) should be made configurable: update
the handler in app/api/repo/route.ts (the function that constructs this body) to
accept an optional ignore list from the incoming request payload (e.g.,
request.body.ignore) and/or read a fallback from an environment variable (e.g.,
REPO_IGNORE_LIST as JSON or comma-separated); if neither is provided, fall back
to the current default array. Validate/parses the env or request value into an
array before including it in JSON.stringify so existing behavior remains
unchanged when no config is supplied.

In `@app/components/toolbar.tsx`:
- Line 64: The title attribute for the download button ("downloadImage") is
using camelCase while other buttons use Title Case, causing inconsistent
accessibility labels; update the title attribute in the toolbar component (the
element with title="downloadImage") to use Title Case e.g. "Download Image" to
match "Zoom In"/"Zoom Out"/"Center" and ensure consistency across the toolbar
UI.

In `@e2e/logic/utils.ts`:
- Around line 52-54: The findNodeByName function currently uses any[] and any;
replace these with a type-safe signature by introducing a Node-like shape or a
generic: define or use an interface with optional fields name?: string and
data?: { name?: string } and change findNodeByName to accept nodes of that type
(or a generic T extends that shape) and return T | undefined instead of any;
update any callsites if necessary to satisfy the new return type.
- Around line 16-35: waitForStableText currently returns the last-read
stableText even if the loop timed out without stabilization; update the end of
waitForStableText to treat timeout as a failure: throw an Error (or at minimum
log a warning) indicating the text never stabilized, include contextual info
(the last observed stableText, locator description or string, and the timeout
value) so tests can fail fast and debugging is easier; locate this logic in the
waitForStableText function and replace the final return stableText with a thrown
error or a process/test logger call as appropriate for the test runner.

In `@lib/utils.ts`:
- Line 2: The import includes an unused symbol GraphNode from `@falkordb/canvas`;
remove GraphNode from the import statement (leave FalkorDBCanvas) to eliminate
the unused import warning and keep imports minimal.
- Around line 7-10: The PathData type currently uses any[] for nodes and links
which reduces type safety; update the PathData definition in lib/utils.ts to use
the specific imported Node and Link types (i.e., nodes: Node[] and links:
Link[]), add/import those types if they are not already imported, and adjust any
call sites or tests that relied on any[] to satisfy the stronger types.
- Around line 31-36: The Message interface's paths property is weakly typed
using any[]; replace it with the existing PathData type for stronger typing:
change paths?: { nodes: any[], links: any[] }[] to paths?: PathData[] (or
Array<PathData>) in the Message interface so it uses the PathData definition
above and preserves optionality. Ensure PathData is visible in the same module
(or imported) so the Message type compiles.

In `@package.json`:
- Line 29: Add the DefinitelyTyped declarations for D3 by adding "@types/d3":
"^7.4.3" to devDependencies in package.json so TypeScript has typings when d3 is
imported; update the package.json devDependencies block (referencing the
existing "d3" dependency) and run npm/yarn install to persist the change.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fea017b and 1d41240.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (28)
  • .github/dependabot.yml
  • .github/workflows/nextjs.yml
  • .github/workflows/playwright.yml
  • Dockerfile
  • README.md
  • app/api/repo/route.ts
  • app/components/ForceGraph.tsx
  • app/components/chat.tsx
  • app/components/code-graph.tsx
  • app/components/dataPanel.tsx
  • app/components/elementMenu.tsx
  • app/components/graphView.tsx
  • app/components/model.ts
  • app/components/toolbar.tsx
  • app/page.tsx
  • components/ui/switch.tsx
  • e2e/config/testData.ts
  • e2e/infra/ui/basePage.ts
  • e2e/infra/ui/browserWrapper.ts
  • e2e/logic/POM/codeGraph.ts
  • e2e/logic/utils.ts
  • e2e/tests/canvas.spec.ts
  • e2e/tests/chat.spec.ts
  • e2e/tests/nodeDetailsPanel.spec.ts
  • e2e/tests/searchBar.spec.ts
  • lib/utils.ts
  • package.json
  • playwright.config.ts
✅ Files skipped from review due to trivial changes (1)
  • e2e/infra/ui/browserWrapper.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • .github/workflows/playwright.yml
  • app/components/elementMenu.tsx
  • e2e/tests/nodeDetailsPanel.spec.ts


Have questions or feedback? Reach out via:

* [GitHub Issues](https://github.com/FalkorDB/GraphRAG-SDK/issues)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Verify the GitHub Issues link.

The issues link points to https://github.com/FalkorDB/GraphRAG-SDK/issues, but this README is for the code-graph repository. Should this link to https://github.com/FalkorDB/code-graph/issues instead?

🔧 Proposed fix
-* [GitHub Issues](https://github.com/FalkorDB/GraphRAG-SDK/issues)
+* [GitHub Issues](https://github.com/FalkorDB/code-graph/issues)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
* [GitHub Issues](https://github.com/FalkorDB/GraphRAG-SDK/issues)
* [GitHub Issues](https://github.com/FalkorDB/code-graph/issues)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` at line 109, Update the GitHub Issues link in the README: replace
the current URL "https://github.com/FalkorDB/GraphRAG-SDK/issues" referenced by
the "[GitHub Issues]" list item with
"https://github.com/FalkorDB/code-graph/issues" so the repo's README points to
the correct issue tracker for code-graph.

@barakb
Copy link

barakb commented Mar 2, 2026

@coderabbitai full review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 2, 2026

✅ Actions performed

Full review triggered.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/components/chat.tsx (1)

64-69: ⚠️ Potential issue | 🟡 Minor

Missing paths in useEffect dependency array.

The effect accesses paths to find the selected path, but paths is not in the dependency array. This could cause stale closure issues where updates to paths don't trigger re-evaluation.

Proposed fix
     useEffect(() => {
         const p = paths.find((path) => [...path.links, ...path.nodes].some((e: any) => e.id === selectedPathId))

         if (!p) return
         handleSetSelectedPath(p)
-    }, [selectedPathId])
+    }, [selectedPathId, paths])

Note: You may also need to add handleSetSelectedPath to the dependency array or wrap it in useCallback.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/chat.tsx` around lines 64 - 69, The effect using useEffect
reads `paths` to find the selected path but only lists `selectedPathId` in its
dependency array, causing stale closures; update the dependency array for that
useEffect to include `paths` (and also include or memoize
`handleSetSelectedPath`) so the effect re-runs whenever `paths` or the setter
change; alternatively wrap `handleSetSelectedPath` in useCallback and include it
in the dependency list to avoid unnecessary re-renders.
♻️ Duplicate comments (9)
playwright.config.ts (1)

22-23: ⚠️ Potential issue | 🟡 Minor

Update the stale CI parallelism comment.
Line 22 says CI opts out of parallel tests, but Line 23 explicitly sets 3 workers on CI.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@playwright.config.ts` around lines 22 - 23, The comment above the Playwright
config is stale: it claims CI opts out of parallel tests but the workers
property explicitly sets workers: process.env.CI ? 3 : undefined. Fix by making
them consistent: either change the comment to state that CI runs with 3 workers
(e.g., "Use 3 workers on CI") or change the code to opt out (set workers:
process.env.CI ? undefined : undefined or workers: process.env.CI ? 1 :
undefined) depending on intent; update the comment near the workers property to
accurately describe the behavior of the workers setting.
README.md (2)

109-109: ⚠️ Potential issue | 🟡 Minor

Fix the GitHub Issues link.

The link points to GraphRAG-SDK but this README is for the code-graph repository.

🔧 Proposed fix
-* [GitHub Issues](https://github.com/FalkorDB/GraphRAG-SDK/issues)
+* [GitHub Issues](https://github.com/FalkorDB/code-graph/issues)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` at line 109, Update the GitHub Issues link in README.md: replace
the existing link text "[GitHub
Issues](https://github.com/FalkorDB/GraphRAG-SDK/issues)" so it points to the
code-graph repository issues URL (e.g.,
https://github.com/FalkorDB/code-graph/issues), ensuring the "[GitHub Issues]"
entry now references the correct repository; keep the link label unchanged.

9-9: ⚠️ Potential issue | 🟡 Minor

Remove stray character.

Line 9 contains a lone - which appears to be a leftover from editing.

🔧 Proposed fix
-
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` at line 9, Remove the stray lone '-' character found in the README
(the isolated dash on line 9) by deleting that character so the file contains
only intended content; ensure no other stray punctuation remains and run a quick
preview to confirm formatting is unchanged.
app/globals.css (1)

132-139: ⚠️ Potential issue | 🟡 Minor

Remove duplicate @layer base declaration.

This block duplicates the @layer base declaration at lines 85-94. Additionally, line 137 misses font-roberto that exists in the original at line 91.

🔧 Proposed fix
-@layer base {
-  * {
-    `@apply` border-border;
-  }
-  body {
-    `@apply` bg-background text-foreground;
-  }
-}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/globals.css` around lines 132 - 139, Remove the duplicate `@layer` base
block and merge its rules into the existing `@layer` base (avoid repeating the
`@layer` base declaration); specifically ensure the body selector inside the
retained `@layer` base includes the missing font utility by adding the
font-roberto utility to the existing body `@apply` (so body uses bg-background
text-foreground and font-roberto) and keep the universal * { `@apply`
border-border } rule in that single `@layer` base.
app/components/dataPanel.tsx (1)

21-27: ⚠️ Potential issue | 🟡 Minor

Remove duplicated isPathSelected from excludedProperties.

The same key appears twice, which is redundant and easy to miss in future edits.

Suggested fix
     "curve",
     "__indexColor",
-    "isPathSelected",
     "__controlPoints",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/dataPanel.tsx` around lines 21 - 27, The excludedProperties
array contains a duplicate string "isPathSelected"; remove the redundant
"isPathSelected" entry from the excludedProperties definition (leave a single
occurrence) to avoid redundancy and potential confusion when editing, locating
the array by the symbol name excludedProperties in dataPanel.tsx and updating
that list accordingly.
e2e/tests/canvas.spec.ts (1)

222-225: ⚠️ Potential issue | 🟡 Minor

Remove unsafe non-null assertions before API/path assertions.

Lines 222, 224, and 225 still rely on ! for node ids; this was already flagged and remains brittle.

Suggested fix
       expect(firstNodeRes).toBeDefined();
       expect(secondNodeRes).toBeDefined();
+      if (!firstNodeRes || !secondNodeRes) throw new Error("Path nodes were not found in graph payload");

       const api = new ApiCalls();
-      const response = await api.showPath(GRAPHRAG_SDK ,firstNodeRes!.id, secondNodeRes!.id);
+      const response = await api.showPath(GRAPHRAG_SDK, firstNodeRes.id, secondNodeRes.id);
       const callsRelationObject = response.result.paths[0].find(item => item.relation === "CALLS")
-      expect(callsRelationObject?.src_node).toBe(firstNodeRes!.id);
-      expect(callsRelationObject?.dest_node).toBe(secondNodeRes!.id);
+      expect(callsRelationObject?.src_node).toBe(firstNodeRes.id);
+      expect(callsRelationObject?.dest_node).toBe(secondNodeRes.id);
#!/bin/bash
# Verify remaining non-null id assertions in canvas tests
rg -nP '!\.id' e2e/tests/canvas.spec.ts
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/tests/canvas.spec.ts` around lines 222 - 225, Replace unsafe non-null
assertions by asserting presence and guarding before accessing ids: before
calling api.showPath(GRAPHRAG_SDK, firstNodeRes!.id, secondNodeRes!.id) ensure
firstNodeRes and secondNodeRes are defined (e.g.,
expect(firstNodeRes).toBeDefined(); expect(secondNodeRes).toBeDefined()) and
then call showPath with their .id without using `!`; after getting response,
assert response.result.paths exists and has at least one path, find
callsRelationObject, and assert callsRelationObject is defined before checking
callsRelationObject.src_node and callsRelationObject.dest_node (use
expect(callsRelationObject).toBeDefined();
expect(callsRelationObject!.src_node).toBe(firstNodeRes.id); etc.).
e2e/tests/chat.spec.ts (1)

69-87: ⚠️ Potential issue | 🟡 Minor

Avoid non-null assertions after optional chaining in regex extraction.

Lines 69 and 85 can fail with unclear errors when the regex does not match.

Suggested fix
       const result = await chat.getTextInLastChatElement();
-      const number = result.match(/\d+/g)?.[0]!;
+      const match = result.match(/\d+/g);
+      expect(match?.[0]).toBeDefined();
+      const number = match![0];
@@
     const uiResponse = await chat.getTextInLastChatElement();
-    const number = uiResponse.match(/\d+/g)?.[0]!;
-    
-    expect(number).toEqual(apiResponse.result.response.match(/\d+/g)?.[0]);
+    const uiMatch = uiResponse.match(/\d+/g);
+    const apiMatch = apiResponse.result.response.match(/\d+/g);
+    expect(uiMatch?.[0]).toBeDefined();
+    expect(apiMatch?.[0]).toBeDefined();
+    expect(uiMatch![0]).toEqual(apiMatch![0]);
#!/bin/bash
# Verify remaining optional-chain non-null assertions in this file
rg -nP '\.match\(/\\d\+\/g\)\?\.\[0\]!' e2e/tests/chat.spec.ts
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/tests/chat.spec.ts` around lines 69 - 87, The regex extractions using
optional chaining plus non-null assertion (e.g., uiResponse.match(/\d+/g)?.[0]!
and apiResponse.result.response.match(/\d+/g)?.[0]!) can throw unclear runtime
errors when there's no match; update the test to safely handle missing matches
by removing the non-null assertions and checking that the match exists before
using it: capture the match arrays (from uiResponse.match and
apiResponse.result.response.match), assert they are defined/contain at least one
element (or fail the test with a clear message) and then compare the first
element; look for the variables uiResponse, apiResponse, number and the test
"Validate UI response matches API response for a given question in chat" to
apply this change.
package.json (1)

33-33: ⚠️ Potential issue | 🟠 Major

next version concern appears unresolved from prior review.

Line 33 still targets ^15.5.8, which was previously flagged for advisories. Please move to the first patched 15.x version (or newer supported line).

Check official Next.js advisories/release notes for next@15.5.8 and identify the first patched 15.x version that resolves those advisories.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` at line 33, Update the "next" dependency in package.json (the
"next" entry currently set to "^15.5.8") to the first patched 15.x release that
resolves the flagged advisories (or to a newer supported release line if
preferred); consult Next.js release notes/security advisories to identify that
exact patched 15.x version and replace the version string accordingly, then run
your lockfile update (npm/yarn/pnpm) and verify the new version is installed and
tests/builds succeed.
app/components/code-graph.tsx (1)

106-110: ⚠️ Potential issue | 🟠 Major

Fix Delete-key handling for single selection and link deletion.

Line 108 is still inverted, and Line 109 always deletes as "nodes" even when selectedObj is a Link. This blocks valid single-delete flows and can target the wrong entity type.

Proposed fix
         const handleKeyDown = (event: KeyboardEvent) => {
             if (event.key === 'Delete') {
-                if (selectedObj && selectedObjects.length === 0) return
-                handleRemove([...selectedObjects.map(obj => obj.id), selectedObj?.id].filter(id => id !== undefined), "nodes");
+                if (!selectedObj && selectedObjects.length === 0) return
+
+                const isSelectedObjLink = !!selectedObj && "source" in selectedObj
+                if (isSelectedObjLink && selectedObjects.length === 0) {
+                    handleRemove([selectedObj.id], "links")
+                    return
+                }
+
+                const nodeIds = [
+                    ...selectedObjects.map(obj => obj.id),
+                    ...(!isSelectedObjLink && selectedObj ? [selectedObj.id] : []),
+                ]
+                if (nodeIds.length === 0) return
+                handleRemove(nodeIds, "nodes")
             }
         };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/code-graph.tsx` around lines 106 - 110, The Delete-key handler
in handleKeyDown incorrectly returns when a single selection exists and always
calls handleRemove with "nodes"; invert the early-return check so it returns
only when there is no selection (i.e., if (!selectedObj &&
selectedObjects.length === 0) return), and determine the correct removal type by
inspecting the selected item(s): if selectedObj?.type === "Link" (or instanceof
Link depending on your model) call handleRemove(..., "links"), otherwise call
handleRemove(..., "nodes"); when mixing selections, compute ids and types
accordingly (e.g., map selectedObjects to their ids and ensure you pass the
appropriate entity type rather than hardcoding "nodes").
🟡 Minor comments (12)
.github/dependabot.yml-13-16 (1)

13-16: ⚠️ Potential issue | 🟡 Minor

Group name doesn't match the configured update types.

The group is named npm-minor-patch, suggesting it should include both minor and patch updates, but only patch is specified in update-types. Either:

  • Add - "minor" if the intent is to group both minor and patch updates, or
  • Rename the group to npm-patch to accurately reflect its scope.
Option A: Include minor updates
     groups:
       npm-minor-patch:
         update-types:
           - "patch"
+          - "minor"
Option B: Rename group to match scope
     groups:
-      npm-minor-patch:
+      npm-patch:
         update-types:
           - "patch"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/dependabot.yml around lines 13 - 16, The group name
"npm-minor-patch" in the dependabot configuration doesn't match its configured
update-types; either add "- \"minor\"" to the update-types list to include minor
and patch updates, or rename the group to "npm-patch" so the group name reflects
that only patch updates are included—update the groups entry for npm-minor-patch
and the update-types key accordingly.
components/ui/carousel.tsx-109-121 (1)

109-121: ⚠️ Potential issue | 🟡 Minor

Missing cleanup for reInit event listener.

The effect adds listeners for both reInit and select events, but the cleanup only removes the select listener. This could cause a memory leak.

🔧 Proposed fix
       return () => {
+        api?.off("reInit", onSelect)
         api?.off("select", onSelect)
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/ui/carousel.tsx` around lines 109 - 121, The effect registers both
"reInit" and "select" listeners on the carousel API but only removes "select" on
cleanup; update the cleanup inside the React.useEffect that references api and
onSelect to also call api?.off("reInit", onSelect) (i.e., remove both listeners)
so both event handlers are detached when the effect tears down.
docker-compose.yml-9-10 (1)

9-10: ⚠️ Potential issue | 🟡 Minor

Review the volume mount scope.

Mounting the entire current directory (./) to /data/ could inadvertently expose sensitive files (e.g., .env, .git, credentials) to the container. Consider mounting only the specific directories needed.

🛡️ Proposed fix to limit volume scope
     volumes:
-      - ./:/data/
+      - ./data:/data/

Create a dedicated data directory for FalkorDB persistence.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker-compose.yml` around lines 9 - 10, The docker-compose volume mounts the
entire repo (./) into the container as /data/, risking exposure of sensitive
files; change the volume to mount only a dedicated data directory (e.g., create
a local "data" folder) or specific required subfolders instead of "./", and
update the volumes entry (the current "- ./:/data/") to reference that dedicated
directory (or explicit paths) so only FalkorDB persistence data is exposed to
the container.
e2e/tests/nodeDetailsPanel.spec.ts-44-50 (1)

44-50: ⚠️ Potential issue | 🟡 Minor

Assert targetNode exists before clicking by coordinates.

In this flow, targetNode is used directly for screenX/screenY; add a guard to avoid accidental runtime failures.

Suggested fix
       const graphData = await codeGraph.getGraphNodes();
       const targetNode = findNodeByName(graphData, node.nodeName);
+      expect(targetNode).toBeDefined();
+      if (!targetNode) throw new Error(`Node not found: ${node.nodeName}`);
       await codeGraph.nodeClick(targetNode.screenX, targetNode.screenY);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/tests/nodeDetailsPanel.spec.ts` around lines 44 - 50, The test uses
targetNode (returned from findNodeByName) directly for coordinates which can be
undefined; add an explicit guard/assert after obtaining graphData and targetNode
(from getGraphNodes and findNodeByName) to fail fast if targetNode is missing
(e.g., expect(targetNode).toBeDefined() or throw a clear Error) before calling
codeGraph.nodeClick(targetNode.screenX, targetNode.screenY), ensuring the test
doesn't crash with an unclear runtime exception.
e2e/logic/utils.ts-16-35 (1)

16-35: ⚠️ Potential issue | 🟡 Minor

waitForStableText can silently return unstable text on timeout.

Line 34 returns the last sampled text even if stabilization never occurred. That weakens test determinism.

Suggested fix
 export const waitForStableText = async (locator: Locator, timeout: number = 5000): Promise<string> => {
@@
-    return stableText;
+    throw new Error(`Text did not stabilize within ${timeout}ms. Last value: "${stableText}"`);
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/logic/utils.ts` around lines 16 - 35, The waitForStableText helper
(function waitForStableText) currently returns the last sampled text on timeout
which can hide flaky failures; change it so that if the loop finishes without
observing a stable, non-empty value it throws an Error (include the last sampled
text and the timeout value in the message for debugging) instead of returning
stableText, and keep the existing pollingInterval/maxChecks logic and the
locator usage (locator.textContent(), locator.page().waitForTimeout()) intact.
e2e/tests/canvas.spec.ts-128-131 (1)

128-131: ⚠️ Potential issue | 🟡 Minor

Guard fallback arrays before reading .length.

If both shapes are missing, nodes/links become undefined and the length checks crash.

Suggested fix
-      const nodes = result.elements?.nodes || result.nodes;
-      const links = result.elements?.links || result.links;
+      const nodes = result.elements?.nodes ?? result.nodes ?? [];
+      const links = result.elements?.links ?? result.links ?? [];
       expect(nodes.length).toBeGreaterThan(1);
       expect(links.length).toBeGreaterThan(1);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/tests/canvas.spec.ts` around lines 128 - 131, The test currently assumes
nodes and links are arrays and reads .length directly, which will throw if both
result.elements?.nodes and result.nodes are undefined; update the extraction to
ensure a safe array fallback (e.g., use nullish coalescing or an explicit array
check) so nodes and links are always arrays before asserting lengths—refer to
the variables nodes and links and the expressions result.elements?.nodes ||
result.nodes (or replace with result.elements?.nodes ?? result.nodes ?? []) and
similarly for links, then assert against the guaranteed array length.
app/components/toolbar.tsx-10-10 (1)

10-10: ⚠️ Potential issue | 🟡 Minor

Unusual type signature for setCooldownTicks.

The type (ticks?: 0) => void only allows undefined or the literal 0. Based on usage in app/page.tsx and app/components/graphView.tsx, the function is called with -1 as well (e.g., setCooldownTicks(-1)). This type should be widened.

Proposed fix
-    setCooldownTicks: (ticks?: 0) => void
+    setCooldownTicks: (ticks: number | undefined) => void
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/toolbar.tsx` at line 10, The type for setCooldownTicks is too
narrow — it currently permits only undefined or the literal 0; update its
signature (in app/components/toolbar.tsx where setCooldownTicks is declared) to
accept a number (e.g., ticks?: number or ticks: number | undefined) so callers
like setCooldownTicks(-1) are allowed; ensure any related usages and prop types
(where ToolbarProps or similar is defined) are updated accordingly to reflect
the widened numeric type.
app/page.tsx-338-346 (1)

338-346: ⚠️ Potential issue | 🟡 Minor

Viewport check may exclude valid canvases.

The visibility check requires the canvas to be fully within the viewport (rect.bottom <= window.innerHeight && rect.right <= window.innerWidth). If the canvas is larger than the viewport or partially scrolled, no canvas will match, causing a silent early return. Consider checking for intersection rather than full containment.

Proposed fix for partial visibility
         // Check if element is actually in viewport
         const rect = container.getBoundingClientRect();
-        const isInViewport = rect.width > 0 &&
-          rect.height > 0 &&
-          rect.top >= 0 &&
-          rect.left >= 0 &&
-          rect.bottom <= window.innerHeight &&
-          rect.right <= window.innerWidth;
+        const isInViewport = rect.width > 0 &&
+          rect.height > 0 &&
+          rect.top < window.innerHeight &&
+          rect.left < window.innerWidth &&
+          rect.bottom > 0 &&
+          rect.right > 0;

         return isInViewport;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/page.tsx` around lines 338 - 346, The current isInViewport check in
app/page.tsx requires full containment and will miss canvases that are larger
than or partially in view; update the logic around the isInViewport calculation
(the rect checks using rect.top/left/bottom/right and
window.innerHeight/window.innerWidth) to test for intersection instead of full
containment: keep rect.width > 0 && rect.height > 0, then require rect.bottom >
0 && rect.right > 0 && rect.top < window.innerHeight && rect.left <
window.innerWidth so a canvas that is partially visible still counts as
in-viewport.
app/components/graphView.tsx-138-138 (1)

138-138: ⚠️ Potential issue | 🟡 Minor

Double-click threshold of 1000ms is unusually long.

The typical double-click threshold is 300-500ms. A 1000ms threshold may cause unintended double-click detection from separate intentional clicks.

Proposed fix
-        const isDoubleClick = now.getTime() - date.getTime() < 1000 && name === node.data.name
+        const isDoubleClick = now.getTime() - date.getTime() < 400 && name === node.data.name
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/graphView.tsx` at line 138, The double-click detection uses a
1000ms window which is too long; update the comparison in the isDoubleClick
computation to use a standard threshold (e.g., 300–500ms) and preferably extract
it to a named constant for clarity. Replace the hardcoded 1000 in the
isDoubleClick expression (where now, date, name and node.data.name are used)
with a constant like DOUBLE_CLICK_THRESHOLD = 500 and compare now.getTime() -
date.getTime() < DOUBLE_CLICK_THRESHOLD so the threshold is easy to adjust and
consistent.
app/components/toolbar.tsx-62-66 (1)

62-66: ⚠️ Potential issue | 🟡 Minor

Guard against undefined handleDownloadImage before invoking.

handleDownloadImage is optional (handleDownloadImage?: () => void), but the onClick handler invokes it without a check. Clicking this button when the prop is undefined will cause a runtime error.

Proposed fix
             <button
                 className="hidden md:block control-button"
                 title="downloadImage"
-                onClick={handleDownloadImage}
+                onClick={() => handleDownloadImage?.()}
             >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/toolbar.tsx` around lines 62 - 66, The Download button calls
the optional handler directly and can throw if undefined; update the onClick
usage in the Toolbar component to guard against undefined handleDownloadImage
(e.g., use a safe call or only attach the handler when present), and consider
disabling the button when handleDownloadImage is not provided so the UI reflects
absence of an action; modify the JSX around the button (the element using
className "hidden md:block control-button" and title "downloadImage") to use
handleDownloadImage?.() or conditionally set onClick/disabled accordingly.
app/components/elementMenu.tsx-125-136 (1)

125-136: ⚠️ Potential issue | 🟡 Minor

Redundant onClick handler on anchor element.

The <a> tag already has href and target="_blank", so the onClick handler that calls window.open with the same URL is redundant and will cause the link to open twice in some browsers.

Proposed fix
                     <a
                         className="p-2"
                         href={objURL}
                         target="_blank"
+                        rel="noopener noreferrer"
                         title="Go to repo"
-                        onClick={() => {
-                            window.open(objURL, '_blank');
-                        }}
                     >
                         <Globe color="white" />
                     </a>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/elementMenu.tsx` around lines 125 - 136, Remove the redundant
onClick handler on the anchor in elementMenu.tsx that calls window.open(objURL,
'_blank'); the <a> already uses href={objURL} and target="_blank", so delete the
onClick={() => { window.open(objURL, '_blank'); }} from the anchor (the element
rendering the Globe icon) to prevent the link from opening twice in some
browsers.
app/components/code-graph.tsx-207-207 (1)

207-207: ⚠️ Potential issue | 🟡 Minor

Use block syntax for forEach callbacks to avoid implicit return values.

Lines 207 and 411 have arrow function callbacks with implicit returns (from Set.add(...) and assignment), making the intent unclear and inconsistent with other forEach calls in the file.

Proposed fixes
-        deleteNeighbors(expandedNodes)?.forEach(id => deleteIdsMap.add(id))
+        deleteNeighbors(expandedNodes)?.forEach(id => {
+            deleteIdsMap.add(id)
+        })
-                                                    graph.Categories.forEach(c => c.show = true);
+                                                    graph.Categories.forEach(c => {
+                                                        c.show = true
+                                                    });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/code-graph.tsx` at line 207, The forEach callbacks use concise
arrow expressions that rely on implicit returns (e.g.,
deleteNeighbors(expandedNodes)?.forEach(id => deleteIdsMap.add(id))), which is
inconsistent and unclear; update these to use block-bodied arrow functions with
explicit statements (e.g., id => { deleteIdsMap.add(id); }) so intent is clear
and matches other forEach usages — locate the occurrences around deleteNeighbors
and the similar callback at line ~411 and replace the concise arrow bodies with
block bodies containing explicit calls or assignments.
🧹 Nitpick comments (9)
e2e/config/constants.ts (1)

3-3: Fix the exported constant typo before it spreads (CHAT_OPTTIONS_COUNT).
Line 3 exports a misspelled public identifier. Consider adding a correctly spelled alias and keeping the old name temporarily for compatibility.

Proposed non-breaking cleanup
-export const CHAT_OPTTIONS_COUNT = 6;
+export const CHAT_OPTIONS_COUNT = 6;
+export const CHAT_OPTTIONS_COUNT = CHAT_OPTIONS_COUNT; // backward compatibility alias
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/config/constants.ts` at line 3, Export a correctly spelled constant name
and keep the misspelled one as a backward-compatible alias: add a new exported
symbol CHAT_OPTIONS_COUNT with the same value as the existing
CHAT_OPTTIONS_COUNT and re-export the old CHAT_OPTTIONS_COUNT (or assign it from
the new constant) so consumers using the misspelled identifier keep working
while code is migrated to CHAT_OPTIONS_COUNT.
lib/utils.ts (1)

7-10: Avoid any[] in shared graph/message contracts.
Lines 8-9 and Line 34 remove most type safety from the new centralized model. Use concrete types and reuse PathData in Message to prevent drift.

Proposed typing cleanup
 export type PathData = {
-  nodes: any[]
-  links: any[]
+  nodes: Node[]
+  links: Link[]
 }
@@
 export interface Message {
   type: MessageTypes;
   text?: string;
-  paths?: { nodes: any[], links: any[] }[];
+  paths?: PathData[];
   graphName?: string;
 }

Also applies to: 31-35

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/utils.ts` around lines 7 - 10, The PathData type uses any[] for nodes and
links and the Message type (referenced in this file) duplicates loose typing;
replace any[] with concrete, shared types (e.g., define Node and Link interfaces
or reuse existing ones) and update PathData to nodes: Node[] and links: Link[];
then change Message to reference PathData instead of duplicating fields so both
the graph contract and Message reuse the same strongly typed structure (update
all occurrences of PathData, Message, Node, and Link in this module).
components/ui/carousel.tsx (2)

88-99: Keyboard navigation doesn't account for vertical orientation.

The handleKeyDown uses ArrowLeft/ArrowRight regardless of orientation. For vertical carousels, ArrowUp/ArrowDown would be more intuitive.

♻️ Proposed enhancement
     const handleKeyDown = React.useCallback(
       (event: React.KeyboardEvent<HTMLDivElement>) => {
-        if (event.key === "ArrowLeft") {
+        const prevKey = orientation === "horizontal" ? "ArrowLeft" : "ArrowUp"
+        const nextKey = orientation === "horizontal" ? "ArrowRight" : "ArrowDown"
+        if (event.key === prevKey) {
           event.preventDefault()
           scrollPrev()
-        } else if (event.key === "ArrowRight") {
+        } else if (event.key === nextKey) {
           event.preventDefault()
           scrollNext()
         }
       },
-      [scrollPrev, scrollNext]
+      [orientation, scrollPrev, scrollNext]
     )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/ui/carousel.tsx` around lines 88 - 99, handleKeyDown always maps
ArrowLeft/ArrowRight to scrollPrev/scrollNext, which ignores vertical carousels;
update handleKeyDown to branch on the carousel orientation (e.g., an orientation
prop or isVertical flag) and use ArrowUp/ArrowDown for vertical mode while
keeping ArrowLeft/ArrowRight for horizontal mode, still calling scrollPrev and
scrollNext and calling event.preventDefault() for handled keys; locate the
callback by name handleKeyDown and update its dependency array if you reference
any new orientation variable.

208-214: Minor: Remove extra space in className.

There's a double space between absolute and h-8.

🔧 Proposed fix
-        "absolute  h-8 w-8 rounded-full",
+        "absolute h-8 w-8 rounded-full",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/ui/carousel.tsx` around lines 208 - 214, The className string
passed to the cn() call in the Carousel component has an extra space between
"absolute" and "h-8"; update the class string inside the className prop (the
cn(...) expression in components/ui/carousel.tsx where the element builds
"absolute  h-8 w-8 rounded-full") to remove the double space so it becomes
"absolute h-8 w-8 rounded-full".
README.md (1)

3-3: Consider using h2 for consistent heading levels.

The heading jumps from h1 (#) directly to h3 (###). For proper document structure, consider using h2 (##) instead.

📝 Proposed fix
-### Visualize your repository with our graph for code analysis
+## Visualize your repository with our graph for code analysis
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` at line 3, The heading "Visualize your repository with our graph
for code analysis" is an h3 while the document starts with h1; change this
header from "### Visualize your repository with our graph for code analysis" to
an h2 by replacing the three hashes with two ("##") so heading levels are
consistent and follow the h1 -> h2 structure in README.md.
e2e/logic/POM/codeGraph.ts (1)

403-403: Magic number for animation delay.

The 2000ms timeout is a hardcoded magic number. Consider extracting it to a named constant or making it configurable for better maintainability.

Proposed fix
+const GRAPH_ANIMATION_DELAY = 2000;
+
 // In selectGraph method:
-        await this.page.waitForTimeout(2000); // graph animation delay
+        await this.page.waitForTimeout(GRAPH_ANIMATION_DELAY);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/logic/POM/codeGraph.ts` at line 403, The hardcoded 2000ms in the call to
this.page.waitForTimeout(2000) is a magic number; extract it into a named
constant (e.g., ANIMATION_DELAY_MS) or make it configurable (pass as a parameter
or read from a config) and replace the literal in the method in codeGraph.ts so
the delay is maintainable and easy to adjust; update any related tests or usages
to reference the new constant/config option.
app/components/ForceGraph.tsx (2)

154-156: Remove unused canvasRef from handleEngineStop dependencies.

canvasRef is listed in the dependency array but is not used in the callback body. This can cause unnecessary re-creations of the callback.

Proposed fix
     const handleEngineStop = useCallback(() => {
         onEngineStop()
-    }, [canvasRef, onEngineStop])
+    }, [onEngineStop])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/ForceGraph.tsx` around lines 154 - 156, The handleEngineStop
useCallback includes an unused dependency canvasRef which causes needless
re-creations; update the dependency array for handleEngineStop to only include
onEngineStop (i.e., useCallback(() => { onEngineStop() }, [onEngineStop])) so
the callback only re-creates when onEngineStop changes and remove canvasRef from
the dependency list.

106-151: Consider memoizing node/link lookups for better performance.

The event handlers use data.nodes.find() and data.links.find() which are O(n) operations. For large graphs, this could impact performance during rapid interactions like hovering. Consider using a Map for O(1) lookups.

Proposed optimization
+// Add memoized maps
+const nodesMap = useMemo(() => new Map(data.nodes.map(n => [n.id, n])), [data.nodes])
+const linksMap = useMemo(() => new Map(data.links.map(l => [l.id, l])), [data.links])

 // Map node click handler
 const handleNodeClick = useCallback((node: GraphNode, event: MouseEvent) => {
-    const originalNode = data.nodes.find(n => n.id === node.id)
+    const originalNode = nodesMap.get(node.id)
     if (originalNode) onNodeClick(originalNode, event)
-}, [onNodeClick, data.nodes])
+}, [onNodeClick, nodesMap])

Apply the same pattern to all other handlers (handleNodeHover, handleNodeRightClick, handleLinkClick, handleLinkHover, handleLinkRightClick).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/ForceGraph.tsx` around lines 106 - 151, Replace repeated O(n)
searches in handlers by building memoized lookup maps for nodes and links (e.g.,
nodeById and linkById) using useMemo derived from data.nodes and data.links,
then change handleNodeClick, handleNodeHover, handleNodeRightClick,
handleLinkClick, handleLinkHover, and handleLinkRightClick to use
nodeById.get(node.id) / linkById.get(link.id) for O(1) lookups; update each
handler's dependency array to depend on the corresponding memoized map (not the
raw arrays) and preserve the current null-check behavior in
handleNodeHover/handleLinkHover.
app/components/elementMenu.tsx (1)

145-161: Redundant type cast after type guard.

After the "category" in obj check on line 146, TypeScript narrows obj to Node. The explicit cast obj as Node on lines 150 and 156 is redundant and can be removed for cleaner code.

Proposed refactor
             {
                 "category" in obj &&
                 <>
                     <button
                         className="p-2"
-                        onClick={() => handleExpand([obj as Node], true)}
+                        onClick={() => handleExpand([obj], true)}
                     >
                         <Maximize2 color="white" />
                     </button>
                     <button
                         className="p-2"
-                        onClick={() => handleExpand([obj as Node], false)}
+                        onClick={() => handleExpand([obj], false)}
                     >
                         <Minimize2 color="white" />
                     </button>
                 </>
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/elementMenu.tsx` around lines 145 - 161, The "category" in obj
type guard already narrows obj to Node, so remove the redundant casts obj as
Node in the two onClick handlers and pass obj directly to handleExpand; update
both calls inside the fragment (the Maximize2 and Minimize2 buttons) to use
handleExpand([obj], true/false) so the code is cleaner and leverages
TypeScript's narrowing (symbols: obj, Node, handleExpand).

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8a89f17 and 1d41240.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • public/code-graph-logo.svg is excluded by !**/*.svg
📒 Files selected for processing (39)
  • .github/dependabot.yml
  • .github/workflows/nextjs.yml
  • .github/workflows/playwright.yml
  • Dockerfile
  • README.md
  • app/api/repo/route.ts
  • app/components/ForceGraph.tsx
  • app/components/Input.tsx
  • app/components/chat.tsx
  • app/components/code-graph.tsx
  • app/components/combobox.tsx
  • app/components/dataPanel.tsx
  • app/components/elementMenu.tsx
  • app/components/graphView.tsx
  • app/components/model.ts
  • app/components/toolbar.tsx
  • app/globals.css
  • app/layout.tsx
  • app/page.tsx
  • components.json
  • components/ui/carousel.tsx
  • components/ui/drawer.tsx
  • components/ui/switch.tsx
  • docker-compose.yml
  • e2e/config/constants.ts
  • e2e/config/testData.ts
  • e2e/infra/ui/basePage.ts
  • e2e/infra/ui/browserWrapper.ts
  • e2e/logic/POM/codeGraph.ts
  • e2e/logic/utils.ts
  • e2e/tests/canvas.spec.ts
  • e2e/tests/chat.spec.ts
  • e2e/tests/navBar.spec.ts
  • e2e/tests/nodeDetailsPanel.spec.ts
  • e2e/tests/searchBar.spec.ts
  • lib/utils.ts
  • package.json
  • playwright.config.ts
  • tailwind.config.js
💤 Files with no reviewable changes (1)
  • app/layout.tsx

npm run build
NEXTAUTH_SECRET=SECRET npm start &
npx playwright test --shard=${{ matrix.shard }}/2 --reporter=dot,list
NEXTAUTH_SECRET=SECRET npm start & npx playwright test --shard=${{ matrix.shard }}/2 --reporter=dot,list
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 1. Check the exact content of the playwright workflow at the mentioned lines
cat -n .github/workflows/playwright.yml | sed -n '35,45p'

Repository: FalkorDB/code-graph

Length of output: 552


🏁 Script executed:

#!/bin/bash
# 2. Find and examine playwright.config.ts
fd -t f 'playwright.config.*' -x cat -n {} | head -100

Repository: FalkorDB/code-graph

Length of output: 2945


🏁 Script executed:

#!/bin/bash
# 3. Search for any existing readiness wait mechanisms in the workflow
rg -n 'curl|wait-on|webServer|healthcheck' .github/workflows/ --type yml

Repository: FalkorDB/code-graph

Length of output: 90


🏁 Script executed:

#!/bin/bash
# 4. Check for reporter configuration in playwright config
rg -n 'reporter' . --type ts --type js | head -20

Repository: FalkorDB/code-graph

Length of output: 249


Fix CI startup race and prevent Playwright reporter override.

Line 39 starts the app without a readiness check (npm start &) and immediately runs tests, which can cause flaky failures. Additionally, the --reporter=dot,list flag overrides the playwright.config.ts reporter configuration ([["html", { outputFolder: "playwright-report" }]]), preventing HTML report generation while the artifact upload step still expects it.

Suggested workflow fix
-        NEXTAUTH_SECRET=SECRET npm start & npx playwright test --shard=${{ matrix.shard }}/2 --reporter=dot,list
+        NEXTAUTH_SECRET=SECRET npm start &
+        for i in {1..60}; do
+          curl -fsS http://127.0.0.1:3000 >/dev/null && break
+          sleep 1
+        done
+        npx playwright test --shard=${{ matrix.shard }}/2
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
NEXTAUTH_SECRET=SECRET npm start & npx playwright test --shard=${{ matrix.shard }}/2 --reporter=dot,list
NEXTAUTH_SECRET=SECRET npm start &
for i in {1..60}; do
curl -fsS http://127.0.0.1:3000 >/dev/null && break
sleep 1
done
npx playwright test --shard=${{ matrix.shard }}/2
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/playwright.yml at line 39, The CI starts the app in
background with `npm start &` and immediately runs `npx playwright test` (with
`--reporter=dot,list`), causing a race and overriding the reporters defined in
`playwright.config.ts` (which expects the HTML reporter). Fix by replacing the
fire-and-forget start with a readiness wait strategy (e.g., use a
wait-on/start-server-and-test style flow or run `npm start` then wait for the
app URL/port to be reachable before running tests) and remove the
`--reporter=dot,list` flag from the `npx playwright test` invocation so the
reporters in `playwright.config.ts` (HTML output folder) are used and the
subsequent artifact upload will find the HTML report.

Comment on lines +35 to +45
if (!repo_url) {
throw new Error("URL parameter is missing");
}

const { url, token } = getEnvVariables();

const isLocal = repo_url.startsWith("file://")

const result = await fetch(`${url}/${isLocal ? "analyze_folder" : "analyze_repo"}`, {
method: 'POST',
body: JSON.stringify({ repo_url, ignore: ["./.github", "./sbin", "./.git", "./deps", "./bin", "./build"] }),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Block untrusted file:// inputs before selecting analyze_folder.

At Line 41, endpoint selection is fully client-controlled. A caller can submit url=file:///... and trigger local-folder analysis on the server/backend. This should be explicitly gated (or rejected) in this public API route.

🔒 Suggested hardening
 export async function POST(request: NextRequest) {
 
 	const repo_url = request.nextUrl.searchParams.get('url');
 
 	try {
 
 		if (!repo_url) {
 			throw new Error("URL parameter is missing");
 		}
+
+		const parsed = new URL(repo_url);
+		const allowLocalAnalysis = process.env.ENABLE_LOCAL_ANALYSIS === "true";
+		if (parsed.protocol === "file:" && !allowLocalAnalysis) {
+			throw new Error("Local file analysis is disabled");
+		}
+		if (!["file:", "http:", "https:"].includes(parsed.protocol)) {
+			throw new Error("Unsupported URL scheme");
+		}
 
 		const { url, token } = getEnvVariables();
 
-		const isLocal = repo_url.startsWith("file://")
+		const isLocal = parsed.protocol === "file:"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!repo_url) {
throw new Error("URL parameter is missing");
}
const { url, token } = getEnvVariables();
const isLocal = repo_url.startsWith("file://")
const result = await fetch(`${url}/${isLocal ? "analyze_folder" : "analyze_repo"}`, {
method: 'POST',
body: JSON.stringify({ repo_url, ignore: ["./.github", "./sbin", "./.git", "./deps", "./bin", "./build"] }),
if (!repo_url) {
throw new Error("URL parameter is missing");
}
const parsed = new URL(repo_url);
const allowLocalAnalysis = process.env.ENABLE_LOCAL_ANALYSIS === "true";
if (parsed.protocol === "file:" && !allowLocalAnalysis) {
throw new Error("Local file analysis is disabled");
}
if (!["file:", "http:", "https:"].includes(parsed.protocol)) {
throw new Error("Unsupported URL scheme");
}
const { url, token } = getEnvVariables();
const isLocal = parsed.protocol === "file:"
const result = await fetch(`${url}/${isLocal ? "analyze_folder" : "analyze_repo"}`, {
method: 'POST',
body: JSON.stringify({ repo_url, ignore: ["./.github", "./sbin", "./.git", "./deps", "./bin", "./build"] }),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/api/repo/route.ts` around lines 35 - 45, The route currently lets clients
pick analyze_folder vs analyze_repo by sending a file:// URL; update the
validation around repo_url (the variable used when building the fetch call) so
file:// inputs are blocked or require explicit server-side authorization before
selecting analyze_folder. In practice, inside the handler that reads repo_url
and before calling fetch(`${url}/${isLocal ? "analyze_folder" :
"analyze_repo"}`...), enforce that if repo_url.startsWith("file://") you either
return a 400 error or only allow it when a secure server-only condition is met
(e.g., an authenticated admin check or an allowlist flag from
getEnvVariables()); ensure all branches use that validation so analyze_folder
cannot be triggered by arbitrary clients.

Comment on lines +43 to +51
const result = await fetch(`${url}/${isLocal ? "analyze_folder" : "analyze_repo"}`, {
method: 'POST',
body: JSON.stringify({ repo_url, ignore: ["./.github", "./sbin", "./.git", "./deps", "./bin", "./build"] }),
headers: {
"Authorization": token,
'Content-Type': 'application/json'
},
cache: 'no-store'
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Read the specific file to verify the code at lines 43-51
cat -n app/api/repo/route.ts | sed -n '40,55p'

Repository: FalkorDB/code-graph

Length of output: 624


🏁 Script executed:

# Search for signal/timeout patterns in API routes
rg -nP --type=ts 'signal\s*:|AbortSignal|timeout' app/api/

Repository: FalkorDB/code-graph

Length of output: 45


🏁 Script executed:

# Look at all fetch calls in the api routes to understand the pattern
rg -nP --type=ts 'fetch\(' app/api -A 5 -B 1

Repository: FalkorDB/code-graph

Length of output: 4398


Add a timeout to backend fetch to prevent hung requests.

The POST call at lines 43–51 lacks an abort signal, so a stalled backend can indefinitely tie up this route. All fetch calls across the API routes have this vulnerability.

Suggested fix
 		const result = await fetch(`${url}/${isLocal ? "analyze_folder" : "analyze_repo"}`, {
 			method: 'POST',
 			body: JSON.stringify({ repo_url, ignore: ["./.github", "./sbin", "./.git", "./deps", "./bin", "./build"] }),
 			headers: {
 				"Authorization": token,
 				'Content-Type': 'application/json'
 			},
+			signal: AbortSignal.timeout(30_000),
 			cache: 'no-store'
 		});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const result = await fetch(`${url}/${isLocal ? "analyze_folder" : "analyze_repo"}`, {
method: 'POST',
body: JSON.stringify({ repo_url, ignore: ["./.github", "./sbin", "./.git", "./deps", "./bin", "./build"] }),
headers: {
"Authorization": token,
'Content-Type': 'application/json'
},
cache: 'no-store'
});
const result = await fetch(`${url}/${isLocal ? "analyze_folder" : "analyze_repo"}`, {
method: 'POST',
body: JSON.stringify({ repo_url, ignore: ["./.github", "./sbin", "./.git", "./deps", "./bin", "./build"] }),
headers: {
"Authorization": token,
'Content-Type': 'application/json'
},
signal: AbortSignal.timeout(30_000),
cache: 'no-store'
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/api/repo/route.ts` around lines 43 - 51, The fetch call that assigns
result (the POST to `${url}/${isLocal ? "analyze_folder" : "analyze_repo"}`)
needs an AbortController-based timeout so requests can't hang indefinitely:
create an AbortController, start a timer (e.g., 15–30s) that calls
controller.abort(), pass controller.signal in the fetch options (alongside
method, body, headers, cache), and clear the timer after fetch completes; also
catch the abort/DOMException and handle it (return a 504 or throw a clear
error). Update the code around the existing result = await fetch(...) invocation
and ensure the timer is cleared in both success and error paths.

Comment on lines +53 to +60
if (!result.ok) {
throw new Error(await result.text());
}

return NextResponse.json({ message: "success" }, { status: 200 });
} catch (err) {
console.error(err)
return NextResponse.json(err instanceof Error ? err.message : String(err), { status: 400 });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Do not expose raw upstream error bodies to clients.

Lines 54-60 currently propagate backend error text directly in the API response. Return a sanitized message and keep detailed text in server logs only.

🛡️ Suggested error sanitization
 		if (!result.ok) {
-			throw new Error(await result.text());
+			const upstream = await result.text();
+			console.error("Repository analysis failed", { status: result.status, upstream });
+			throw new Error("Repository analysis failed");
 		}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!result.ok) {
throw new Error(await result.text());
}
return NextResponse.json({ message: "success" }, { status: 200 });
} catch (err) {
console.error(err)
return NextResponse.json(err instanceof Error ? err.message : String(err), { status: 400 });
if (!result.ok) {
const upstream = await result.text();
console.error("Repository analysis failed", { status: result.status, upstream });
throw new Error("Repository analysis failed");
}
return NextResponse.json({ message: "success" }, { status: 200 });
} catch (err) {
console.error(err)
return NextResponse.json(err instanceof Error ? err.message : String(err), { status: 400 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/api/repo/route.ts` around lines 53 - 60, The handler currently throws and
returns raw upstream error text (see the throw new Error(await result.text())
and the catch that returns err.message via NextResponse.json), which exposes
sensitive details; instead, in the try/catch keep logging the full upstream
text/stack to server logs (console.error or process logger) but return a
sanitized JSON response via NextResponse.json with a generic message (e.g.,
"Upstream service error" or "Internal server error") and an appropriate status
code; update the throw site (inside the result.ok check) to throw a generic
Error like new Error("Upstream service returned an error") or capture the
upstream body into a local variable for logging, and modify the catch block to
log the full error (and body) and respond with a sanitized message rather than
err.message.

setPaths: Dispatch<SetStateAction<PathData[]>>
}

const PATH_COLOR = "#ffde21";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Duplicate PATH_COLOR constant.

PATH_COLOR is defined here and also exported from lib/utils.ts (line 43). Import it from lib/utils.ts instead to maintain a single source of truth.

Proposed fix
-import { cn, GraphRef } from "@/lib/utils";
+import { cn, GraphRef, PATH_COLOR } from "@/lib/utils";
...
-const PATH_COLOR = "#ffde21";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const PATH_COLOR = "#ffde21";
import { cn, GraphRef, PATH_COLOR } from "@/lib/utils";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/chat.tsx` at line 35, Remove the duplicate local constant
PATH_COLOR in app/components/chat.tsx and instead import the shared PATH_COLOR
exported from lib/utils.ts; locate and delete the line declaring const
PATH_COLOR = "#ffde21" and add an import for PATH_COLOR from 'lib/utils' so all
uses in the Chat component reference the single exported symbol.

Comment on lines +74 to +77
: typeof value === "object" ?
<JSONTree
data={Object.fromEntries(Object.entries(value).filter(([k]) => !excludedProperties.includes(k)))}
theme={{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Object.entries(value) can throw when value is null.

typeof null === "object", so this branch needs a null guard.

Suggested fix
-                                        : typeof value === "object" ?
+                                        : value !== null && typeof value === "object" ?
                                             <JSONTree
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
: typeof value === "object" ?
<JSONTree
data={Object.fromEntries(Object.entries(value).filter(([k]) => !excludedProperties.includes(k)))}
theme={{
: value !== null && typeof value === "object" ?
<JSONTree
data={Object.fromEntries(Object.entries(value).filter(([k]) => !excludedProperties.includes(k)))}
theme={{
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/dataPanel.tsx` around lines 74 - 77, The branch rendering
<JSONTree> assumes value is an object but misses a null guard (since typeof null
=== "object"); update the condition that chooses the <JSONTree> branch to ensure
value is non-null (e.g., check value !== null && typeof value === "object") or
replace Object.entries(value) with Object.entries(value ?? {}) so Object.entries
never receives null; adjust the use around JSONTree, Object.entries, and
excludedProperties to operate only when value is a real object to avoid a
runtime exception.

Comment on lines +133 to +147
<a
className="flex items-center gap-2 p-2"
href={url}
target="_blank"
title="Go to repo"
onClick={() => {
const newTab = window.open(url, '_blank');

if (!obj.src_start || !obj.src_end || !newTab) return
if (!obj.data.src_start || !obj.data.src_end || !newTab) return

newTab.scroll({
top: obj.src_start,
behavior: 'smooth'
})
}}
>
<SquareArrowOutUpRight color="white" />
Go to repo
</a>
</footer>
</div>
newTab.scroll({
top: obj.data.src_start,
behavior: 'smooth'
})
}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid combining href navigation with window.open in onClick.

This can open two tabs (browser default + explicit window.open) and makes navigation behavior inconsistent.

Suggested fix
                             <a
                                 className="flex items-center gap-2 p-2"
-                                href={url}
+                                href={obj.data.src_start ? `${url}#L${obj.data.src_start}` : url}
                                 target="_blank"
+                                rel="noopener noreferrer"
                                 title="Go to repo"
-                                onClick={() => {
-                                    const newTab = window.open(url, '_blank');
-
-                                    if (!obj.data.src_start || !obj.data.src_end || !newTab) return
-
-                                    newTab.scroll({
-                                        top: obj.data.src_start,
-                                        behavior: 'smooth'
-                                    })
-                                }}
                             >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<a
className="flex items-center gap-2 p-2"
href={url}
target="_blank"
title="Go to repo"
onClick={() => {
const newTab = window.open(url, '_blank');
if (!obj.src_start || !obj.src_end || !newTab) return
if (!obj.data.src_start || !obj.data.src_end || !newTab) return
newTab.scroll({
top: obj.src_start,
behavior: 'smooth'
})
}}
>
<SquareArrowOutUpRight color="white" />
Go to repo
</a>
</footer>
</div>
newTab.scroll({
top: obj.data.src_start,
behavior: 'smooth'
})
}}
<a
className="flex items-center gap-2 p-2"
href={obj.data.src_start ? `${url}#L${obj.data.src_start}` : url}
target="_blank"
rel="noopener noreferrer"
title="Go to repo"
>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/dataPanel.tsx` around lines 133 - 147, The anchor combines an
href with an onClick that also calls window.open, causing duplicate tabs; in the
anchor handler (the <a> element using url, target="_blank" and the onClick that
creates newTab) either remove the href/target and use window.open exclusively,
or keep href/target and call event.preventDefault() at the top of the onClick
and then perform a single window.open(url, '_blank') before attempting to scroll
using obj.data.src_start/obj.data.src_end (check newTab truthiness). Update the
code in dataPanel.tsx to use one navigation mechanism and ensure scrolling runs
only on the single opened tab (use newTab.scroll when newTab exists).

Comment on lines +33 to +34
- OPENAI_API_KEY=YOUR_OPENAI_API_KEY
- SECRET_TOKEN=sC0tTerMania
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid hardcoding secrets in docker-compose.yml.

The SECRET_TOKEN has a hardcoded value that will be committed to version control. This is a security risk as secrets should not be stored in plain text in the repository.

Consider using environment variable substitution or a .env file:

🔒 Proposed fix using environment variable substitution
     environment:
       - FALKORDB_HOST=falkordb
       - FALKORDB_PORT=6379
-      - OPENAI_API_KEY=YOUR_OPENAI_API_KEY
-      - SECRET_TOKEN=sC0tTerMania
+      - OPENAI_API_KEY=${OPENAI_API_KEY}
+      - SECRET_TOKEN=${SECRET_TOKEN}
       - FLASK_RUN_HOST=0.0.0.0
       - FLASK_RUN_PORT=5000
       - CODE_GRAPH_PUBLIC=1

Then create a .env.example file with placeholder values and add .env to .gitignore.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- OPENAI_API_KEY=YOUR_OPENAI_API_KEY
- SECRET_TOKEN=sC0tTerMania
- OPENAI_API_KEY=${OPENAI_API_KEY}
- SECRET_TOKEN=${SECRET_TOKEN}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker-compose.yml` around lines 33 - 34, The docker-compose.yml currently
hardcodes SECRET_TOKEN which exposes credentials; change the service environment
to read SECRET_TOKEN from an external source (use environment variable
substitution like ${SECRET_TOKEN} or reference a .env file) and update
documentation by adding a .env.example with a placeholder SECRET_TOKEN and
adding .env to .gitignore; ensure any code/config reading SECRET_TOKEN (e.g.,
the service that expects SECRET_TOKEN) continues to work with the new env
lookup.

Comment on lines +123 to 130
for (let index = 1; index < 6; index++) {
const questionNumber = index + 1;
test(`Validate displaying question ${index} in chat after selection from options menu`, async () => {
const chat = await browser.createNewPage(CodeGraph, urls.baseUrl);
await chat.selectGraph(GRAPHRAG_SDK);
await chat.clickOnQuestionOptionsMenu();
const selectedQuestion = await chat.selectAndGetQuestionInOptionsMenu(questionNumber.toString());
await chat.clickOnLightBulbBtn();
const selectedQuestion = await chat.selectAndGetQuestionInOptionsMenu(questionNumber.toString());
expect(selectedQuestion).toEqual(await chat.getLastQuestionInChat())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Question-option loop is off by one.

Line 123 starts at 1, and Line 124 adds +1, so the selected option range becomes 2..6 (skips first option and can exceed available options).

Suggested fix
-  for (let index = 1; index < 6; index++) {
-    const questionNumber = index + 1;
+  for (let index = 1; index <= CHAT_OPTTIONS_COUNT; index++) {
+    const questionNumber = index;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/tests/chat.spec.ts` around lines 123 - 130, The loop computes
questionNumber incorrectly by adding +1, causing it to select options 2..6 and
skip the first option; change the assignment so questionNumber equals the loop
index (i.e., let questionNumber = index) or remove the +1 usage so calls to
chat.selectAndGetQuestionInOptionsMenu(questionNumber.toString()) and the
corresponding expect compare the intended option (ensure the loop bounds still
match available options). This touches the for loop and the variable
questionNumber in the test and the call to
chat.selectAndGetQuestionInOptionsMenu/getLastQuestionInChat.

Comment on lines 76 to +80
const graphData = await codeGraph.getGraphNodes();

// Find a node that has src property (actual source code)
const targetNode = graphData.find(node => node.src) || graphData[0];
const nodeName = targetNode.name || targetNode.data?.name;
await codeGraph.fillSearchBar(nodeName);
await codeGraph.selectSearchBarOptionBtn("1");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Guard empty graph state before deriving nodeName.

If graphData is empty, Line 78 dereferences undefined and the test fails before validating panel behavior.

Suggested fix
     const graphData = await codeGraph.getGraphNodes();
+    expect(graphData.length).toBeGreaterThan(0);
     const targetNode = graphData.find(node => node.src) || graphData[0];
     const nodeName = targetNode.name || targetNode.data?.name;
+    expect(nodeName).toBeDefined();
+    if (!nodeName) throw new Error("Unable to resolve node name for search");
     await codeGraph.fillSearchBar(nodeName);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const graphData = await codeGraph.getGraphNodes();
// Find a node that has src property (actual source code)
const targetNode = graphData.find(node => node.src) || graphData[0];
const nodeName = targetNode.name || targetNode.data?.name;
await codeGraph.fillSearchBar(nodeName);
await codeGraph.selectSearchBarOptionBtn("1");
const graphData = await codeGraph.getGraphNodes();
expect(graphData.length).toBeGreaterThan(0);
const targetNode = graphData.find(node => node.src) || graphData[0];
const nodeName = targetNode.name || targetNode.data?.name;
expect(nodeName).toBeDefined();
if (!nodeName) throw new Error("Unable to resolve node name for search");
await codeGraph.fillSearchBar(nodeName);
await codeGraph.selectSearchBarOptionBtn("1");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/tests/nodeDetailsPanel.spec.ts` around lines 76 - 80, The test assumes
graphData from codeGraph.getGraphNodes() is non-empty; guard against an empty
array before deriving targetNode/nodeName by checking graphData.length (or that
targetNode is defined) and handling the empty case (e.g., fail early with a
clear message or skip the subsequent steps). Update the block around
codeGraph.getGraphNodes(), targetNode, and nodeName to validate graphData and
targetNode and only call codeGraph.fillSearchBar(nodeName) and
codeGraph.selectSearchBarOptionBtn("1") when a valid nodeName exists; reference
the existing functions codeGraph.getGraphNodes, the local targetNode/nodeName
variables, and the methods fillSearchBar and selectSearchBarOptionBtn to locate
and apply the guard.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants